﻿using NetCommon;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ServerFrame
{
    /// <summary>
    /// 客户端标记，一个对象就是一个客户端
    /// </summary>
    public class UserToken
    {
        /// <summary>
        /// 客户端套接字，通过该对象进行收发数据
        /// </summary>
        public Socket socket;

        /// <summary>
        /// receSAEA:负责数据接收
        /// sendSAEA:负责数据发送
        /// </summary>
        SocketAsyncEventArgs receSAEA;
        SocketAsyncEventArgs sendSAEA;

        /// <summary>
        /// 数据接收缓存
        /// </summary>
        List<byte> ReceCache = new List<byte>();
        bool isReceive = false;

        /// <summary>
        /// 数据发送队列（队列：先进先出）
        /// </summary>
        Queue<byte[]> sendQueue = new Queue<byte[]>();
        bool isSend = false;

        /// <summary>
        /// 客户端离开服务器的委托
        /// </summary>
        /// <param name="token"></param>
        /// <param name="error"></param>
        public delegate void ClientClose(UserToken token, string error);
        public ClientClose CloseProcess;
        /// <summary>
        /// 在启动服务器的接口处进行赋值
        /// </summary>
        public static ServerSetting setting;
        public UserToken()
        {
            receSAEA = new SocketAsyncEventArgs();
            sendSAEA = new SocketAsyncEventArgs();

            receSAEA.Completed += IO_Completed;
            sendSAEA.Completed += IO_Completed;

            //设置数据接收的缓存，必须设置
            receSAEA.SetBuffer(new byte[1024], 0,1024);

        }

        private void IO_Completed(object sender, SocketAsyncEventArgs saea)
        {
            //saea只监听了收发事件，所以不是收数据就是发数据
            if (saea.LastOperation == SocketAsyncOperation.Receive)
            {
                //监听接收
                ProcessReceive(saea);
            }
            else
            {
                //监听发送
                ProcessSend(saea);
            }
        }


        /// <summary>
        /// 开启数据接收
        /// </summary>
        public void StartReceive()
        {
            try
            {
                //为用户开启数据接收
                bool ret = socket.ReceiveAsync(receSAEA);
                if (!ret)
                {
                    //处理接收到的数据
                    ProcessReceive(receSAEA);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
            
        }

        /// <summary>
        /// 处理接收到的数据
        /// </summary>
        private void ProcessReceive(SocketAsyncEventArgs saea)
        {
            //进行数据拷贝，开启下一次数据接收
            //saea.BytesTransferred > 0 且状态时Success:数据正常、连接正常
            //saea.BytesTransferred <= 0 且状态时Success:客户端主动断开了链接；强制关闭游戏、强制结束进程等
            //网络异常：状态不是Success，客户端断开链接；拔网线、断电导致服务器无法预知
            //需要添加心跳机制检测
            if (saea.BytesTransferred > 0 && saea.SocketError == SocketError.Success)
            {//消息的长度不为0，且成功通信
                //数据正常，链接正常
                //Console.WriteLine("数据正常， 连接正常， 开始接收数据");
                Receive();
                StartReceive();
            }
            else
            {
                string error;
                if (saea.SocketError != SocketError.Success)
                {
                    error = saea.SocketError.ToString();
                }
                else
                {
                    error = "客户端主动断开了链接";
                }
                if(CloseProcess != null)
                {
                    CloseProcess(this, error);
                }
            }
        }

        /// <summary>
        /// 缓存收到的数据
        /// </summary>
        private void Receive()
        {
            //定义一个字节数组，长度同接收数据的长度
            byte[] msg = new byte[receSAEA.BytesTransferred];
            //接收到的消息，拷贝到数组中
            Buffer.BlockCopy(receSAEA.Buffer, 0, msg, 0, receSAEA.BytesTransferred);//缓存消息、符号、存储数组、偏移、数据长度
            Console.WriteLine("收到用户：" + socket.RemoteEndPoint + " 数据长度：" + msg.Length);
            //将msg加入到接收数据的缓存，等待处理
            Receive(msg);
        }

        private void Receive(byte[] msg)
        {
            //把接收到的数据缓存起来
            ReceCache.AddRange(msg);
            if(isReceive == false)
            {

                isReceive = true;
                //StartReceive();//尾递归，接收下一条消息
                OnReceive();
            }
        }
        /// <summary>
        /// 处理缓存数据
        /// </summary>
        private void OnReceive()
        {
            byte[] buff = null;
            //（可选）长度解码
            if (setting.lengthDecode != null)
            {
                //说明收到的消息是有长度编码的
                buff = setting.lengthDecode(ref ReceCache);
                //如果没有收到一个完整的消息，或者这个消息已经读完了
                if (buff == null)
                {
                    Console.WriteLine("----------如果没有收到一个完整的消息，或者这个消息已经读完了----------");
                    isReceive = false;
                    return;
                }
            }
            else
            {
                //收到的消息是没有 长度编码的
                if (ReceCache.Count == 0)//缓存
                {
                    isReceive = false;
                    return;
                }
                //取出缓存里面所有的数据
                buff = ReceCache.ToArray();
                ReceCache.Clear();
            }

            //（可选）消息解密
            if (setting.messageDecrypt != null)
            {
                buff = DEncrypt.Decrypt(buff);

            }

            //（可选）消息解压
            if (setting.compressDecode != null)
            {
                buff = setting.compressDecode(buff);
            }

            //（必选）消息解码-- 反序列化（从二进制转成object， 会用到传输模型）
            //消息协议——传输模型=模块号 + 命令 + 数据
            if (setting.messageDecode == null)
            {
                throw new Exception("消息需要解码，解码器不能为空");
            }
            //object message = MessageEncoding.Decode(buff);
            object message = setting.messageDecode(buff);
            //Console.WriteLine(((NetModel)message).ToString());
            setting.center.ReceiveMessage(this, ((NetModel)message));

            //尾递归
            OnReceive();
        }


        /// <summary>
        /// 处理发送数据
        /// </summary>
        /// <param name="saea"></param>
        private void ProcessSend(SocketAsyncEventArgs saea)
        {
            //网络异常
            if (saea.SocketError != SocketError.Success)
            {
                //断开该客户端的连接
                if (CloseProcess != null)
                {
                    CloseProcess(this, saea.SocketError.ToString()); 
                }
            }
            else
            {
                Onsend();
            }
        }

        /// <summary>
        /// 发送一个传输模型
        /// </summary>
        /// <param name="model"></param>
        public void SendNetModel(NetModel model)
        {
            byte[] buff = null;

            //消息编码 -- 必要的
            if (setting.messageEncode == null)
            {
                throw new Exception("必须有消息编码");
            }
            buff = setting.messageEncode(model);

            //消息压缩 -- 可选的
            if (setting.compressEncode != null)
            {
                buff = setting.compressEncode(buff);
            }

            //消息加密 -- 可选的
            if (setting.messageEncrypt != null)
            {
                buff = DEncrypt.Encrypt(buff);
            }

            //长度编码 -- 可选的
            if (setting.lengthEncode != null)
            {
                setting.lengthEncode(buff);
            }
        }

        /// <summary>
        /// 应用层数据发送接口，客户端调用
        /// </summary>
        /// <param name="buff"></param>
        public void Send(byte[] buff)
        {
            if(socket == null)
            {//客户端断开
                if (CloseProcess != null)
                {
                    CloseProcess(this, "该用户网络链接断开 socket == nul");
                }
                return;
            }
            //将要发送的数据加入到队列里面，等待处理
            sendQueue.Enqueue(buff);
            if (isSend == false)
            {
                isSend = true;
                Onsend();
            }
        }
        /// <summary>
        /// 处理发送队列中的数据
        /// </summary>
        private void Onsend()
        {
            if (sendQueue.Count == 0)
            {
                isSend = false;
                return;
            }
            //取出队列里面的第一条数据
            byte[] buff = sendQueue.Dequeue();
            sendSAEA.SetBuffer(buff, 0, buff.Length);
            //启动实际数据发送功能
            StartSend();
        }

        /// <summary>
        /// 开启数据发送
        /// </summary>
        private void StartSend()
        {
            //发送数据
            bool ret = socket.SendAsync(sendSAEA);
            if (!ret)
            {
                //处理数据发送
                ProcessSend(sendSAEA);
            }
        }
        /// <summary>
        /// 执行用户断开后的清理工作
        /// </summary>
        public void Close()
        {
            isReceive = false;
            isSend = false;

            ReceCache.Clear();//清空缓存
            sendQueue.Clear();//清空发送队列
            try
            {
                //关闭socket，避免报错程序崩溃
                if (socket != null)
                {
                    socket.Shutdown(SocketShutdown.Both);//关闭socket里面所有的功能
                    socket.Close();//关闭socket
                    socket = null;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw;
            }
            
        }
    }
}
